package com.wufun.overseas.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.os.Environment;
import android.text.TextUtils;



import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Created by xiet on 2015/12/29.
 */
public class ImageCompressor {

    private static int coreSize = 2;
    private static ThreadPoolExecutor exec = new CompressThreadPoolExecutor(coreSize, coreSize, 500, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

    public static long PIC_LIMIT_SIZE_UPPER = (long) (0.3 * 1024 * 1024);//300KB
    public static long PIC_LIMIT_SIZE_LOWER = (long) (0.1 * 1024 * 1024);//100KB
    public static Bitmap.CompressFormat PIC_FORMATE = Bitmap.CompressFormat.JPEG;//

    public Context context;
    private static ImageCompressor instance;
    public Hashtable<String, Integer> compressState = new Hashtable<String, Integer>();//-1-not_process,0-processing, 1-process success, 2-process failed, 3-asynctask canceled
    public Hashtable<String, String> compressMap = new Hashtable<String, String>();



    public ImageCompressor() {
        super();
    }

    public static ImageCompressor getInstance() {
        if (instance == null) {
            instance = new ImageCompressor();
        }

        return instance;
    }

    public synchronized void processImageCompress(final String path, final boolean addTask) {
        if (addTask) {
            addSequence(path);
            exec.execute(new Runnable() {
                @Override
                public void run() {
                    notifyCompressSate(path, 0);
                }
            });
        } else {
            removeSequence(path);
        }
    }

    public void addSequence(String path) {
        notifyCompressSate(path, -1);
    }

    public void removeSequence(String path) {
        notifyCompressSate(path, 3);
    }

    public void compressImage(Context context, String path, boolean addTask) {
//        Logger.log("compressImage-->" + "start notify task.." + path + " addTask=" + addTask);
        this.context = context;

        try {
            processImageCompress(path, addTask);
        } catch (Exception e) {
            e.printStackTrace();
        }

//        if (addTask) {
//            notifyCompressSate(path, 0);
//        } else {
//            notifyCompressSate(path, 3);
//        }
//        Logger.log("compressImage-->" + "finish notify task.." + path + " addTask=" + addTask);
    }

    public void notifyCompressSate(String src, int state) {
        try {
//            Logger.log("notifyCompressSate-->" + "start notifyCompressSate.." + src + " state=" + state);
            if (compressMap.containsKey(src)) {
                compressState.put(src, state);
            }

            switch (state) {
                case -1:
                    if (!compressMap.containsKey(src)) {
                        final String destPath = getMGCacheForumImageDir() + "/" + System.currentTimeMillis() + "/" + StringUtils.getNameNoExt(src) + ".jpg";
//                        Logger.log("notifyCompressSate-->" + "put target to map.. destPath=" + destPath + "");
                        compressMap.put(src, destPath);
                        compressState.put(src, 0);
                    }
                    break;
                case 0:
                    if (!compressMap.containsKey(src)) {
                        final String destPath = getMGCacheForumImageDir() + "/" + System.currentTimeMillis() + "/" + StringUtils.getNameNoExt(src) + ".jpg";
//                        Logger.log("notifyCompressSate-->" + "put target to map.. destPath=" + destPath + "");
                        compressMap.put(src, destPath);
                        compressState.put(src, 0);

                        int quality = 40;//def
                        long size = 0;//init
                        try {
                            size = getFileSize(new File(src));
//                            Logger.log("notifyCompressSate-->" + "src file size=" + size);

                            long useSystemThreshold = (long) (PIC_LIMIT_SIZE_UPPER + 0.7 * 1024 * 1024);//1MB
                            if (size < useSystemThreshold) {
//                                Logger.log("notifyCompressSate-->" + "use system compress tool..");

                                doCompressUseSystemTool(src, destPath, quality);
                            } else {
//                                Logger.log("notifyCompressSate-->" + "use jpegbither compress tool..");
                                quality = 40;//def-40
                                doCompress1(src, destPath, quality);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                    } else {
                        notifyCompressSate(src, 3);
                        notifyCompressSate(src, 0);
                    }
                    break;
                case 1:
                case 2:
                    if (compressMap.containsKey(src)) {
                        pullCompressState();
                    }
                    break;
                case 3:
                    if (compressMap.containsKey(src)) {
                        String destPath = compressMap.get(src);
                        File file = new File(destPath);
                        File pFile = file.getParentFile();
                        if (pFile.exists()) deleteDir(pFile);
                        compressMap.remove(src);
                        compressState.remove(src);
                    }
                    break;
            }
//            Logger.log("notifyCompressSate-->" + "finished notifyCompressSate.." + src + " state=" + state);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void pullCompressState() {
        boolean allState = checkAllTaskState();
        if (allState) {
            notifyAllTaskFinished(allState, compressMap, compressState);
        }
    }



    private void notifyAllTaskFinished(Boolean allFinished, Map<String, String> compressMap, Map<String, Integer> compressState) {
        if (mCompressListener != null) {
            Iterator<CompressListener> iterator = mCompressListener.iterator();
            while (iterator.hasNext()) {
                CompressListener listener = iterator.next();
                listener.onCompress(allFinished, compressMap, compressState);
            }
        }
    }



    public Boolean checkAllTaskState() {
        boolean ret = false;
        Iterator<Map.Entry<String, Integer>> iterator = compressState.entrySet().iterator();
        if (iterator.hasNext()) {
            while (iterator.hasNext()) {
                int state = iterator.next().getValue();
                if (state == 1 || state == 2) {
                } else {
                    return ret = false;
                }
            }
            ret = true;
        } else {
            ret = true;
        }
        return ret;
    }

    public void doCompressUseSystemTool(String src, final String destPath, Integer quality) {
        long size = 0;
        try {
            size = getFileSize(new File(src));

            quality = 100;
            while (size > PIC_LIMIT_SIZE_UPPER) {
//                Logger.log("notifyCompressSate-->" + "system compress tool start..", "quality=" + quality + " size=" + size);

                if (quality > 10) {
//                                    if (size - ForumUtil.UPLOAD_PIC_LIMIT_SIZE_UPPER < 0.1 * 1024 * 1024)
//                                        quality -= 5;
//                                    else
                    quality -= 10;

                    PictureUtil.saveBitmap2File(PictureUtil.getimage(src), destPath, quality);
                    size = getFileSize(new File(destPath));
                } else {
//                    Logger.log("notifyCompressSate-->" + "system compress tool =>", "result=fail");
                    notifyCompressSate(src, 2);
                }
            }

            if (quality == 100)
                PictureUtil.saveBitmap2File(PictureUtil.getimage(src), destPath, quality);

            //check size again
            size = getFileSize(new File(destPath));
            while (size > PIC_LIMIT_SIZE_UPPER) {
//                Logger.log("notifyCompressSate-->" + "system compress tool start..", "quality=" + quality + " size=" + size);

                if (quality > 10) {
//                                    if (size - ForumUtil.UPLOAD_PIC_LIMIT_SIZE_UPPER < 0.1 * 1024 * 1024)
//                                        quality -= 5;
//                                    else
                    quality -= 10;

                    PictureUtil.saveBitmap2File(PictureUtil.getimage(src), destPath, quality);
                    size = getFileSize(new File(destPath));
                } else {
//                    Logger.log("notifyCompressSate-->" + "system compress tool =>", "result=fail");
                    notifyCompressSate(src, 2);
                }
            }

//            Logger.log("notifyCompressSate-->" + "system compress tool =>", "quality=" + quality, "result=success");
            notifyCompressSate(src, 1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void doCompress1(String src, final String destPath, final Integer quality) {
//        Logger.log("doCompress1-->" + "start compress.." + src);

        final boolean optim = true;
        try {

            NativeUtil.compressImage(src, destPath, quality, optim, new NativeUtil.NativeCompressListener() {

                @Override
                public void onSuccess(String src) {
//                    Logger.log("doCompress1-->onSuccess==>" + "finish compress.. success ", src);

                    long size = 0;
                    try {
                        size = getFileSize(new File(destPath));

                        if (size < PIC_LIMIT_SIZE_UPPER) {
//                            Logger.log("doCompress1-->" + "finish compress.. success. size=" + size);
                            notifyCompressSate(src, 1);
                        } else {
                            if (quality - 10 > 20) {
                                int newInt = Integer.valueOf(quality);
                                int newQuality = newInt - 10;
                                boolean newOptim = optim;
//                                Logger.log("doCompress1-->" + "re-compress.. size=" + size, " newQuality=" + newQuality, " newInt=" + newInt);
                                doCompress1(src, destPath, newQuality);
                            } else {
//                                Logger.log("doCompress1-->" + "finish compress.. fail. size=" + size);
                                notifyCompressSate(src, 2);
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

//                if (compressMap.containsKey(src))
//                    checkSizeAndResizeImage(src, compressMap.get(src));
                }

                @Override
                public void onFailed(String src) {
//                    Logger.log("doCompress1-->onFailed==>" + "finish compress.. fail");
                    notifyCompressSate(src, 2);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void doCompress(final Context context, final String src, final String destDir, final int quality) {
        String tool = context.getFilesDir() + "/" + JpegOptimizer.TOOL_NAME;
        if (!new File(tool).exists()) {
            JpegOptimizer.extract(context, tool);
        }
        File destDirFile = new File(destDir);
        if (!destDirFile.exists()) destDirFile.mkdirs();
        boolean preserve = true;
        int threshold = 1;
        JpegOptimizer optimizer = new JpegOptimizer(tool, src, destDir, threshold, quality, preserve);

        JpegOptimizer.OptimResultHandler optimHandler = new JpegOptimizer.OptimResultHandler() {
            @Override
            public void onResult(boolean optimized, String msg) {
//                Logger.log("OptimResultHandler-->msg:" + msg);
                if (optimized) {
                    // doExec﹕/sdcard/Download/1.jpg,1164x3176,24bit,N,13503273,164081,98.78,optimized
                    String[] results = msg.split(",");
                    String src = results[0];
                    String dest = destDir + "/" + new File(src).getName();
                    if (results.length == 8 && Integer.parseInt(results[5]) < PIC_LIMIT_SIZE_UPPER) {
                        optimSuccess(src);
                    } else if (quality > 10) {
                        int size = Integer.parseInt(results[5]);
                        int newQuality = 10;
                        if (size - PIC_LIMIT_SIZE_UPPER < 1024 * 1024 * 1.0) {
                            newQuality = quality - 5;
                        } else {
                            newQuality = quality - 10;
                        }
                        doCompress(context, src, destDir, newQuality);
                    } else {
                        optimSuccess(src);
                    }
                } else {
                    optimFailure(src);
                }
            }

            private void optimSuccess(String src) {
//                Logger.log("OptimResultHandler-->" + "optimSuccess");
                notifyCompressSate(src, 1);

                if (compressMap.containsKey(src))
                    checkSizeAndResizeImage(src, compressMap.get(src));

            }

            void optimFailure(String src) {
//                Logger.log("OptimResultHandler-->" + "optimFailure");
                notifyCompressSate(src, 2);
            }

        };

        optimizer.doOptim(optimHandler);
    }

    private void checkSizeAndResizeImage(String src, String destPath) {
        try {
            if (compressMap.containsKey(src)) {
                long size = getFileSize(new File(destPath));
                if (size < PIC_LIMIT_SIZE_UPPER) {
                    notifyCompressSate(src, 1);
                } else {
                    PictureUtil.saveBitmap2File(PictureUtil.getimage(src), destPath);
                    if (getFileSize(new File(destPath)) < PIC_LIMIT_SIZE_UPPER) {
                        notifyCompressSate(src, 1);
                    } else {
                        notifyCompressSate(src, 2);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static boolean deleteDir(File dir) {
//        Logger.log("deleteDir-->" + "start delete dir " + dir + " ...");
        if (dir.isDirectory() && isCacheDir(dir + "")) {
            String[] children = dir.list(); //递归删除目录中的子目录下
            for (int i = 0; i < children.length; i++) {
//                Logger.log("deleteDir-->" + "delete dir sub file " + children[i] + " ...");
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
        // 目录此时为空，可以删除

//        Logger.log("deleteDir-->" + "finished delete dir " + dir + " ...");
        return dir.delete();
    }

    private static boolean isCacheDir(String path) {
        boolean ret = false;
        if (!TextUtils.isEmpty(path) && path.startsWith(getMGCacheForumImageDir() + "")) ret = true;
        return ret;
    }

    List<CompressListener> mCompressListener;

    public void addCompressListener(CompressListener l) {
        if (mCompressListener == null) {
            mCompressListener = new ArrayList<CompressListener>();
        }

        if (!mCompressListener.contains(l))
            mCompressListener.add(l);
    }

    public void removeCompressListener(CompressListener l) {
        if (mCompressListener != null && mCompressListener.contains(l)) {
            mCompressListener.remove(l);
        }
    }

    public interface CompressListener {
        void onCompress(Boolean allFinished, Map<String, String> compressMap, Map<String, Integer> compressState);
    }

    private static int calculateOptimQuality(long size) {
        int ret = 100;
        float maxThreshold = PIC_LIMIT_SIZE_UPPER;

        float MB = 1.0f * 1024 * 1024;

        float diff = size - maxThreshold;
        if (diff < MB) {
            ret -= 20;
        } else {
            ret -= 80;
        }

        if (ret < 1) {
            ret = 5;
        }

        return ret;
    }

    public interface ImageOptimListener {
        void optimFinished(List<String> optimedImages);
    }

    private static boolean isOptimFinished(HashMap<String, String> map) {
        Collection<String> collection = map.values();
        Iterator<String> iterator = collection.iterator();

        while (iterator.hasNext()) {
            String value = iterator.next();
            if (StringUtils.isEmpty(value)) {
                return false;
            }
        }

        return true;
    }

    private static void notifyOptimFinished(List<String> srcImages, HashMap<String, String> tempHash, ImageOptimListener optimListener) {
        List<String> optimedImages = new ArrayList<String>();
        for (int j = 0; j < srcImages.size(); j++) {
            String optimedImage = tempHash.get(StringUtils.getNameNoExt(srcImages.get(j)));
            optimedImages.add(optimedImage);
        }
        optimListener.optimFinished(optimedImages);
    }

    private static void saveOptimizedImage(final Context context, final String src, final String destDir, final int quality, final List<String> srcImages, final HashMap<String, String> tempHash, final ImageOptimListener optimListener) {
        String tool = context.getFilesDir() + "/" + JpegOptimizer.TOOL_NAME;
        if (!new File(tool).exists()) {
            JpegOptimizer.extract(context, tool);
        }
        boolean preserve = true;
        int threshold = 1;
        JpegOptimizer optimizer = new JpegOptimizer(tool, src, destDir, threshold, quality, preserve);

        JpegOptimizer.OptimResultHandler optimHandler = new JpegOptimizer.OptimResultHandler() {
            @Override
            public void onResult(boolean optimized, String msg) {
//                Logger.log("OptimResultHandler-->msg:" + msg);
                if (optimized) {
                    // doExec﹕/sdcard/Download/1.jpg,1164x3176,24bit,N,13503273,164081,98.78,optimized
                    String[] results = msg.split(",");
                    String src = results[0];
                    if (results.length == 8 && Integer.parseInt(results[5]) < PIC_LIMIT_SIZE_UPPER) {
                        String dest = destDir + "/" + new File(src).getName();

                        optimSuccess(src, dest);
                    } else if (quality > 10) {
                        int size = Integer.parseInt(results[5]);
                        int newQuality = 10;
                        if (size - PIC_LIMIT_SIZE_UPPER < 1024 * 1024 * 1.0) {
                            newQuality = quality - 5;
                        } else {
                            newQuality = quality - 10;
                        }
                        saveOptimizedImage(context, src, destDir, newQuality, srcImages, tempHash, optimListener);
                    } else {
                        optimFailure();
                    }
                } else {
                    optimFailure();
                }
            }

            private void optimSuccess(String src, String dest) {
//                Logger.log("OptimResultHandler-->" + "optimSuccess");
                tempHash.put(StringUtils.getNameNoExt(src), dest);
                if (isOptimFinished(tempHash) && optimListener != null) {
                    //notify
                    notifyOptimFinished(srcImages, tempHash, optimListener);
                } else {

                }
            }

            void optimFailure() {
//                Logger.log("OptimResultHandler-->" + "optimFailure");
            }

        };

        optimizer.doOptim(optimHandler);
    }

    public static void saveRevitionImage(String path, String outPath, int inSampleSize) {
        if (path != null) {
            FileOutputStream fos = null;
            try {
                Bitmap bm = PictureUtil.revitionImageSize(path, inSampleSize);

                if (bm == null) {
                    saveRevitionImage(path, outPath, inSampleSize - 1);
                    return;
                }

//                Log.e("saveRevitionImage", "path=" + path + " outPath=" + outPath + " inSampleSize=" + inSampleSize);
                fos = new FileOutputStream(outPath);
                bm.compress(PIC_FORMATE, 100, fos);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fos != null)
                        fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取指定文件大小
     *
     * @param file
     * @return
     * @throws Exception
     */
    public static long getFileSize(File file) throws Exception {
        long size = 0;
        if (file.exists()) {
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(file);
                size = fis.available();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (fis != null) {
                    fis.close();
                }
            }
        } else {
            if (file != null) {
                File mFolder = file.getParentFile();
                if (!mFolder.exists()) mFolder.mkdirs();
                file.createNewFile();
            }
        }
        return size;
    }

    /**
     * 获取保存图片的目录
     *
     * @return
     */
    public static File getMGCacheForumImageDir() {
        File dir = new File(Environment.getExternalStorageDirectory(), "MG/.cache/" + getForumImageDirName());
        if (!dir.exists()) {
            dir.mkdirs();
        }
        return dir;
    }

    /**
     * 获取保存 隐患检查的图片文件夹名称
     *
     * @return
     */
    public static String getForumImageDirName() {
        return "forum_image";
    }
}
